#ifndef __DANIEL_CONFIG_H__
#define __DANIEL_CONFIG_H__

#include <string>
#include <yaml-cpp/yaml.h>
#include <boost/lexical_cast.hpp>
#include <memory>
#include <vector>
#include <set>
#include <list>
#include <map>
#include <sstream>
#include <util.h>
#include <unordered_map>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <functional>

namespace daniel
{
    //解析基类
    class ConfigBase
    {
    public:
        using ptr=std::shared_ptr<ConfigBase>;
        ConfigBase(const std::string &name, const std::string &description) : m_name(name), m_description(description)
        {
        }
        virtual ~ConfigBase(){};
        std::string getName() const { return m_name; }
        void setName(const std::string &name) { m_name = name; }
        std::string getDescription() const { return m_description; }
        void setDescription(const std::string &description) { m_description = description; }
        //由该类转为yaml格式的string
        virtual std::string toString() = 0;
        //由yaml格式的string转换为该类
        virtual bool fromString(const std::string &str) = 0;

    protected:
        //配置项目的名字和描述
        std::string m_name;
        std::string m_description;
    };
    //普通类型的转换，直接调用boost中的Lexical_cast万能转换函数
    template <typename F, typename T>
    class LexicalCast
    {
    public:
        T operator()(const F &val)
        {
            
            T retVal = boost::lexical_cast<T, F>(val);
            return retVal;
        }
    };
    //对vector容器进行偏特化处理
    template <typename F>
    class LexicalCast<std::vector<F>, std::string>
    {
    public:
        std::string operator()(const std::vector<F> &val)
        {
            YAML::Node node(YAML::NodeType::Sequence);
            std::stringstream ss;
            for (const F &item : val)
            {
                node.push_back(YAML::Load(LexicalCast<F, std::string>()(item)));
            }
            ss << node;
            return ss.str();
        }
    };
    template <typename T>
    class LexicalCast<std::string, std::vector<T>>
    {
    public:
        std::vector<T> operator()(const std::string &str)
        {
            YAML::Node node = YAML::Load(str);
            std::vector<T> retVec;
            std::stringstream ss;
            if (node.IsSequence())
            {
                for (auto it = node.begin(); it != node.end(); it++)
                {
                    ss.str("");
                    ss << *it;
                    retVec.push_back(LexicalCast<std::string, T>()(ss.str()));
                }
            }
            return retVec;
        }
    };
    //set容器的偏特化
    template <typename F>
    class LexicalCast<std::set<F>, std::string>
    {
    public:
        std::string operator()(const std::set<F> &val)
        {
            YAML::Node node(YAML::NodeType::Sequence);
            std::stringstream ss;
            for(const F& item:val){
                node.push_back(YAML::Load(LexicalCast<F,std::string>()(item)));
            }
            ss<<node;
            return ss.str();
        }
    };
    template <typename T>
    class LexicalCast<std::string, std::set<T>>
    {
    public:
        std::set<T> operator()(const std::string &str)
        {
            auto node=YAML::Load(str);
            std::set<T> retSet;
                std::stringstream ss;
            if (node.IsSequence()){
                for(auto it=node.begin();it!=node.end();it++){
                    ss.str("");
                    ss<<*it;
                    retSet.insert(LexicalCast<std::string,T>()(ss.str));
                }
            }
            return retSet;
        }
    };
        //set容器的偏特化
    template <typename F>
    class LexicalCast<std::unordered_set<F>, std::string>
    {
    public:
        std::string operator()(const std::unordered_set<F> &val)
        {
            YAML::Node node(YAML::NodeType::Sequence);
            std::stringstream ss;
            for(const F& item:val){
                node.push_back(YAML::Load(LexicalCast<F,std::string>()(item)));
            }
            ss<<node;
            return ss.str();
        }
    };
    template <typename T>
    class LexicalCast<std::string, std::unordered_set<T>>
    {
    public:
        std::unordered_set<T> operator()(const std::string &str)
        {
            auto node=YAML::Load(str);
            std::unordered_set<T> retSet;
                std::stringstream ss;
            if (node.IsSequence()){
                for(auto it=node.begin();it!=node.end();it++){
                    ss.str("");
                    ss<<*it;
                    retSet.insert(LexicalCast<std::string,T>()(ss.str));
                }
            }
            return retSet;
        }
    };
    //list容器的偏特化
    template <typename F>
    class LexicalCast<std::list<F>, std::string>
    {
    public:
        std::string operator()(const std::list<F> &val)
        {
            YAML::Node node(YAML::NodeType::Sequence);
            for(const auto& item:val){
                node.push_back(YAML::Load(LexicalCast<F,std::string>()(item)));
            }
            std::stringstream ss;
            ss<<node;
            return ss.str();
        }
    };
    template <typename T>
    class LexicalCast<std::string, std::list<T>>
    {
    public:
        std::list<T> operator()(const std::string &str)
        {
            YAML::Node node=YAML::Load(str);
            std::list<T> retList;
            if (node.IsSequence()){
                std::stringstream ss;
                for(size_t i=0;i<node.size();i++){
                    ss.str("");
                    ss<<node[i];
                    retList.push_back(LexicalCast<std::string, T>()(ss.str()));
                }
            }
            return retList;
        }
    };
    //对map容器的偏特化
    template <typename F>
    class LexicalCast<std::map<std::string, F>, std::string>
    {
    public:
        std::string operator()(const std::map<std::string, F> &val)
        {
            std::stringstream ss;
            YAML::Node node=YAML::Node(YAML::NodeType::Map);
            for(const auto& item:val){
                node[item.first]=YAML::Load(LexicalCast<F,std::string>()(item.second));
            }
            ss<<node;
            return ss.str();
        }
    };
    template <typename T>
    class LexicalCast<std::string, std::map<std::string, T>>
    {
    public:
        std::map<std::string, T> operator()(const std::string &str)
        {
            YAML::Node node=YAML::Load(str);
            std::map<std::string,T> retMap;
            std::stringstream ss;
            for(auto it=node.begin();it!=node.end();it++){
                ss.str("");
                ss<<(it->first);
                std::string key=ss.str();
                ss.str("");
                ss<<(it->second);
                retMap.insert({key,LexicalCast<std::string,T>()(ss.str())});
            }
            return retMap;
        }
    };
    template <typename F>
    class LexicalCast<std::unordered_map<std::string, F>, std::string>
    {
    public:
        std::string operator()(const std::unordered_map<std::string, F> &val)
        {
            std::stringstream ss;
            YAML::Node node=YAML::Node(YAML::NodeType::Map);
            for(const auto& item:val){
                node[item.first]=YAML::Load(LexicalCast<F,std::string>()(item.second));
            }
            ss<<node;
            return ss.str();
        }
    };
    template <typename T>
    class LexicalCast<std::string, std::unordered_map<std::string, T>>
    {
    public:
        std::unordered_map<std::string, T> operator()(const std::string &str)
        {
            YAML::Node node=YAML::Load(str);
            std::unordered_map<std::string,T> retMap;
            std::stringstream ss;
            for(auto it=node.begin();it!=node.end();it++){
                ss.str("");
                ss<<(it->first);
                std::string key=ss.str();
                ss.str("");
                ss<<(it->second);
                retMap.insert({key,LexicalCast<std::string,T>()(ss.str())});
            }
            return retMap;
        }
    };


    //因为yaml的scalar有int，double, bool，string, array，object等多种类型，所以需要使用模板
    template <typename T, typename FromStr = LexicalCast<std::string, T>, typename ToStr = LexicalCast<T, std::string>>
    class ConfigVar : public ConfigBase
    {
    public:
        using ptr=std::shared_ptr<ConfigVar>;
        using onChangeCb=std::function<void (const T& oldVal, const T& newVal)>;
        ConfigVar(const std::string &name, const T &val, const std::string &description) : ConfigBase(name, description), m_value(val) {}
        virtual ~ConfigVar()
        {
        }
        virtual std::string toString() override
        {
            try
            {
                return ToStr()(m_value);
            }
            catch (...)
            {
                std::cout << "LexicalCast toString failed" << std::endl;
            }
            return std::string("");
        }

        virtual bool fromString(const std::string& str) override
        {
            try
            {
                setValue(FromStr()(str)) ;
                return true;
            }
            catch(...)
            {
                std::cout << "LexicalCast fromString failed!" << std::endl;
            }
            return false;
        }
        void setValue(const T& val) {m_value=val;}
        T getValue()const {return m_value;}
        //T改变时候的回调函数，用于动态更改配置
        void addCbs(const onChangeCb& cb)
        {
            m_cbs.push_back(cb);
            return;
        }
        void clearCbs()
        {
            m_cbs.clear();
        }
    private:
        T m_value;
        std::list<onChangeCb> m_cbs;
    };

    //配置管理类
    class Config
    {
    public:
        Config();
        ~Config();
        using configVarMap=std::unordered_map<std::string, ConfigBase::ptr>;
        //
        template <typename T>
        typename ConfigVar<T>::ptr lookUp(const std::string& name)
        {
            auto it=m_configs.find(name);
            if (it==m_configs.end()) return nullptr;
            else{
                //dynamic_cast是转换指针的，dynamic_pointer_cast转换智能指针
                return std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
            }
        }
        //插入的类
        template<typename T>
        typename ConfigVar<T>::ptr lookUp(const std::string& name,const T& val, const std::string& description="")
        {
            auto it=m_configs.find(name);
            //说明这个项目存在，如果类型相同，更新值；如果类型不同，报错
            if (it!=m_configs.end()){
                auto temp=it->second;
                //调用回调函数
                if (temp!=nullptr){
                    std::cout<<"the config already exists!"<<std::endl;
                    return std::dynamic_pointer_cast<ConfigVar<T> >(temp);
                }
                else{
                    std::cout<<"the config exists, but value is null!"<<std::endl;
                    return nullptr;
                }
            }
            //说明配置不存在，插入该项配置
            if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyz._0123456789")!=std::string::npos){
                std::cout<<"name is invalid, insert failed"<<std::endl;
                return nullptr;
            }
            typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, val,description));
            m_configs[name]=v;
            return v;
        }
        ConfigBase::ptr lookupBase(const std::string& name);
        void listAllMember(const std::string& prefix, const YAML::Node& node, std::list<std::pair<std::string,const YAML::Node>>& output);
        // void loadFromFile(const std::string& fileName);
        void loadFromFile(const std::string& fileName);
        //从配置文件夹中加载所有的配置文件(后缀为 .yml)
        void loadFromDir(const std::string& dirName);
        void printAll();

    private:
        Config(const Config&)=delete;
        Config operator=(const Config&)=delete;
    private:
        std::string m_fileName;
        configVarMap m_configs;
    };

    using ConfigMsg=daniel::Singleton<Config>;

} // namespace daniel

#endif